﻿using Games.Common;
using Games.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Games.PegSolitaire
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PegSolitarie : ContentPage
    {
        IDataBase database = DependencyService.Get<IDataBase>();
        double cellSize;
        int CellSpacing = 10;
        GameCell selectCell;
        //bool hasStep = false;
        private bool isOver;
        double layoutWidth, layoutHeight;
        Game game = new Game();
        GameInfo gameInfo;
        GameCell current;
        double xMargin, yMargin;
        private Dictionary<int, GameCell> cells = new Dictionary<int, GameCell>();
        internal PegSolitarie(GameInfo gamePage)
        {
            InitializeComponent();
            BindingContext = gameInfo = gamePage;
            database.CreateTable<PsRecord>();
        }
        void OnLayoutSizeChanged(object sender, EventArgs args)
        {
            Layout layout = sender as Layout;
            layoutWidth = layout.Width;
            layoutHeight = layout.Height;
            var sizew = layoutWidth / Game.LENGTH;
            var sizeh = layoutHeight / Game.LENGTH;
            cellSize = (int)Math.Min(sizew, sizeh);
            var laySize = Game.LENGTH * cellSize;

            xMargin = (layoutWidth - laySize + CellSpacing) / 2;
            yMargin = (layoutHeight - laySize + CellSpacing) / 2;
            //xMargin = (layoutWidth - CellSpacing - laySize) / 2.0 + CellSpacing;
            //yMargin = (layoutHeight - CellSpacing - laySize) / 2.0 + CellSpacing;

            InitLayout();
        }
        void InitLayout()
        {
            isOver = false;
            game.Init();
            gameInfo.Step = 0;
            gameInfo.ChessCount = game.Count;
            var chesses = game.Deconstruct().ToList();

            if(absoluteLayout.Children.Count == 0)
            {
                while(absoluteLayout.Children.Count < chesses.Count)
                {
                    GameCell lifeCell = new GameCell();
                    lifeCell.Tapped += OnTapGestureTapped;
                    absoluteLayout.Children.Add(lifeCell);
                }
                current = new GameCell() { Source = GameCell.ImgSelect };
                absoluteLayout.Children.Add(current);
            }

            int index = 0;
            foreach(var item in chesses)
            {
                GameCell lifeCell = (GameCell)absoluteLayout.Children[index];
                lifeCell.Col = item.Item1;
                lifeCell.Row = item.Item2;
                lifeCell.HasChess = item.Item3 == 1;
                lifeCell.Opacity = 1;
                Rectangle rect = new Rectangle(item.Item1 * cellSize + xMargin, item.Item2 * cellSize + yMargin,
                    cellSize - CellSpacing, cellSize - CellSpacing);
                AbsoluteLayout.SetLayoutBounds(lifeCell, rect);
                lifeCell.Show();
                var key = lifeCell.Row * Game.LENGTH + lifeCell.Col;
                if(!cells.ContainsKey(key))
                {
                    cells.Add(lifeCell.Row * Game.LENGTH + lifeCell.Col, lifeCell);
                }
                index++;
            }
            selectCell = null;
            //hasStep = false;
            current.Opacity = 0;
            TimeCounter.Start();
        }

        private GameCell GetCell(int x, int y)
        {
            return cells[y * Game.LENGTH + x];
        }
        async void OnTapGestureTapped(object sender, EventArgs args)
        {
            if(isOver)
            {
                return;
            }
            GameCell lifeCell = (GameCell)sender;
            if(lifeCell == current)
            {
                return;
            }
            if(lifeCell.HasChess)
            {
                if(selectCell != lifeCell)
                {
                    selectCell = lifeCell;
                    var fromBound = AbsoluteLayout.GetLayoutBounds(selectCell);
                    AbsoluteLayout.SetLayoutBounds(current, fromBound);
                    current.Opacity = 1;
                    return;
                }
            }
            if(selectCell == null)
            {
                return;
            }
            if(game.Jump(selectCell.Col, selectCell.Row, lifeCell.Col, lifeCell.Row))
            {
                selectCell.HasChess = false;
                var toBound = AbsoluteLayout.GetLayoutBounds(lifeCell);
                var skip = GetCell((selectCell.Col + lifeCell.Col) / 2, (selectCell.Row + lifeCell.Row) / 2);
                await Task.WhenAny(Move(toBound.X, toBound.Y), skip.FadeTo(0, 200));
                skip.HasChess = false;
                lifeCell.HasChess = true;
                lifeCell.Opacity = 1;
                selectCell = lifeCell;
                gameInfo.Step = game.Step;
                gameInfo.ChessCount = game.Count;
                if(!game.CanMove())
                {
                    isOver = true;
                    var perfect = game.Count == 1 && lifeCell.Col == 3 && lifeCell.Row == 3;
                    await ShowFinish(perfect);
                }
            }
        }
        async void TapGestureRecognizer_Tapped(object sender, EventArgs e)
        {
            if(isOver || gameInfo.Step <= 0)
            {
                return;
            }
            if(game.Back(out int x, out int y, out int tox, out int toy))
            {
                var from = GetCell(x, y);
                var to = GetCell(tox, toy);
                var fromBound = AbsoluteLayout.GetLayoutBounds(to);
                AbsoluteLayout.SetLayoutBounds(current, fromBound);
                var skip = GetCell((x + tox) / 2, (y + toy) / 2);
                var toBound = AbsoluteLayout.GetLayoutBounds(from);
                to.HasChess = false;
                await Task.WhenAny(Move(toBound.X, toBound.Y, 50), skip.FadeTo(1, 100));
                from.HasChess = true;
                skip.HasChess = true;
                gameInfo.Step = game.Step;
                gameInfo.ChessCount = game.Count;
                selectCell = from;
            }
        }
        async Task Move(double tox, double toy, uint length = 100)
        {
            var fromBound = AbsoluteLayout.GetLayoutBounds(current);
            var fromx = fromBound.X;
            var fromy = fromBound.Y;
            var taskCompletionSource = new TaskCompletionSource<bool>();
            current.Animate<Rectangle>(
                "Push",
                t => new Rectangle(fromx + t * (tox - fromx), fromy + t * (toy - fromy), fromBound.Width, fromBound.Height),
                r => AbsoluteLayout.SetLayoutBounds(current, r),
                16,
                length,
                Easing.Linear,
                (r, b) => taskCompletionSource.SetResult(b)
            );
            await current.ScaleBack(1.6, length);
        }
        async Task ShowFinish(bool perfect = false)
        {
            isOver = true;
            var record = await SaveRecordAsync(perfect);
            await DisplayAlert(record.Level, $"{record.MessageString}", "OK");
        }
        async Task<Record> SaveRecordAsync(bool perfect = false)
        {
            var cRecord = new PsRecord()
            {
                GameID = gameInfo.ID,
                Step = gameInfo.Step,
                ChessCount = gameInfo.ChessCount,
                Perfect = perfect,
                CreateTime = DateTime.Now,
                Duration = TimeCounter.Stop()
            };

            await database.SaveRecordByDiff(cRecord, 100,
                o => true,
                list => list.OrderByDescending(x => x.Perfect).ThenBy(x => x.ChessCount).ThenBy(x => x.Step).ThenBy(x => x.Duration).ThenBy(x => x.ID),
                o =>
                {
                    if(cRecord.Perfect && !o.Perfect)
                        return true;
                    if(cRecord.ChessCount < o.ChessCount)
                        return true;
                    if(cRecord.Step < o.Step)
                        return true;
                    if(cRecord.Duration < o.Duration)
                        return true;
                    return false;
                }
            );
            return cRecord.Convert();
        }

        void RestartClicked(object sender, EventArgs args)
        {
            InitLayout();
        }

        void OnAboutButtonClicked(object sender, EventArgs args)
        {
            if(!mask.Contains("about"))
            {
                var aboutView = ControlUtil.GetAbout(gameInfo);
                mask.Add("about", gameInfo.Name, aboutView);
            }
            mask.ShowWithCancel("about", "确定");
        }
        void OnRecordClicked(object sender, EventArgs args)
        {
            //var sorter = new Sorter<PsRecord>(q => q.OrderByDescending(x => x.Perfect).ThenBy(x => x.ChessCount).ThenBy(x => x.Step).ThenBy(x => x.Duration).ThenByDescending(x => x.ID));
            Navigation.PushAsync(new RecordPage(gameInfo.Name, new RecordViewModel<PsRecord>(null)));
        }

        private void TapToBack(object sender, EventArgs e)
        {
            Navigation.PopAsync();
        }
    }
}